package com.routz.nettydemo.customprotocol.coder;

import com.routz.nettydemo.customprotocol.entity.BraceletData;
import com.routz.nettydemo.customprotocol.entity.BraceletDataPacket;
import com.routz.nettydemo.customprotocol.entity.ChickenProtocol;
import com.routz.nettydemo.customprotocol.entity.DeviceInfo;
import com.routz.nettydemo.customprotocol.entity.GPRS;
import com.routz.nettydemo.customprotocol.entity.Location;
import com.routz.nettydemo.customprotocol.entity.PacketHead;
import com.routz.nettydemo.customprotocol.entity.PacketTail;
import com.routz.nettydemo.customprotocol.util.Constant;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.util.concurrent.FastThreadLocal;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Optional;
import java.util.TreeSet;

public class ChickenDecoder extends ByteToMessageDecoder {
    private static final FastThreadLocal<ChickenProtocol> threadLocal = new FastThreadLocal();

    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf in, List<Object> list) throws Exception {
        ChickenProtocol decoded = decode(channelHandlerContext, in);
        if (decoded != null) {
            list.add(decoded);
        }
    }

    private ChickenProtocol decode(ChannelHandlerContext channelHandlerContext, ByteBuf in) {
        int SIZE = in.readableBytes();
        System.out.println("总数据数量: " + SIZE);
        if (SIZE < Constant.DeviceSize) {
            return null;
        }

        ChickenProtocol protocol = Optional
                .ofNullable(threadLocal.get())
                .orElse(new ChickenProtocol());

        byte[] data = new byte[SIZE];
        in.readBytes(data);
        // 将原始数据写出
        write(data);
        ByteBuf buffer = Unpooled.copiedBuffer(data);

        if (SIZE < Constant.PacketSize) {
            fillDeviceInfo(protocol, data, buffer);
        } else {
            fillBraceletDataPacket(protocol, data, buffer);
//            protocol.setBraceletDataPackets(braceletDataPackets);
        }
        TreeSet<BraceletDataPacket> braceletDataPackets = protocol.getBraceletDataPackets();
        if (braceletDataPackets != null && braceletDataPackets.size() == protocol.getBraceletDataCapacity()) {
            threadLocal.remove();
            return protocol;
        } else {
            threadLocal.set(protocol);
            return null;
        }
    }

    private static final String PATH = "/home/javasvr/chicken/data.txt";
//    private static final String PATH = "D:/data.txt";

    private void write(byte[] data) {
        try (BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream(
                        new File(PATH)));) {
            bos.write(data);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void fillBraceletDataPacket(ChickenProtocol protocol, byte[] data, ByteBuf buffer) {
        // 脚环信息
        TreeSet<BraceletDataPacket> braceletDataPackets = protocol.getBraceletDataPackets();
        // 包头
        int braceletDataHeadIndex = buffer.readerIndex();
        byte headSign = buffer.readByte();
        if (headSign != Constant.HeadSign) {
            throw new IllegalArgumentException("开始字节不对");
        }
        byte cmdTypeBD = buffer.readByte();
        if (cmdTypeBD != Constant.CmdBd) {
            throw new IllegalArgumentException("脚环 命令类型不对");
        }
        final byte packetSize = buffer.readByte();
        final byte packetNum = buffer.readByte();

        if (braceletDataPackets == null && packetNum != 0x01) {
            throw new IllegalArgumentException("不是第一包但前面的包不存在");
        } else if (braceletDataPackets != null && packetSize != protocol.getBraceletDataCapacity()) {
            throw new IllegalArgumentException("包数不对 包编号：" + packetNum);
        }
        if (braceletDataPackets == null) {
            protocol.setBraceletDataCapacity(packetSize);
            braceletDataPackets = new TreeSet<>((BraceletDataPacket o1, BraceletDataPacket o2) ->
                    java.util.Optional.ofNullable(o1)
                            .map(o -> o.getPacketHead().getPacketNum())
                            .orElseThrow(() -> new IllegalArgumentException())
                            - java.util.Optional.ofNullable(o1)
                            .map(o -> o.getPacketHead().getPacketNum())
                            .orElseThrow(() -> new IllegalArgumentException()));
            protocol.setBraceletDataPackets(braceletDataPackets);
        }

        byte endSign = buffer.readByte();
        // 2 保留字
        buffer.readByte();
        buffer.readByte();
        String cardLastNum1 = buffer.readCharSequence(Constant.CardLastNum1, Charset.forName("UTF-8")).toString();
        PacketHead packetHead = new PacketHead(headSign, cmdTypeBD, packetSize, packetNum, endSign);

        // 脚环
        BraceletData[] braceletDatas = new BraceletData[Constant.BraceletDataSize];
        for (int i = 0; i < braceletDatas.length; i++) {
            byte[] braceletNumB = new byte[Constant.BraceletNumSize];
            String braceletNum = getStringBuffer(buffer, braceletNumB);
            byte keep2 = buffer.readByte();
            byte[] stepB = new byte[Constant.StepSize];
            String step = getStringBuffer(buffer, stepB);
            byte temperatureB = buffer.readByte();
            java.math.BigDecimal temperature = transferTemperature(temperatureB);
            byte vol_dist = buffer.readByte();
            java.math.BigDecimal voltage = transferVoltage(vol_dist);
            Integer distance = transferDistance(vol_dist);
            BraceletData braceletData = new BraceletData(braceletNum, keep2, step, temperature, voltage, distance);
            braceletDatas[i] = braceletData;
        }

        // 包尾
        String cardLastNum2 = buffer.readCharSequence(Constant.CardLastNum2, Charset.forName("UTF-8")).toString();
        byte braceletSize = buffer.readByte();
        byte tailSignPacket = buffer.readByte();
        int braceletDataTailIndex = buffer.readerIndex();
        int checksum2 = checksum(data, buffer, braceletDataHeadIndex, braceletDataTailIndex);

        // 后6位
        PacketTail packetTail = new PacketTail(braceletSize, tailSignPacket, checksum2, cardLastNum1 + cardLastNum2);

        BraceletDataPacket braceletDataPacket = new BraceletDataPacket(packetHead, braceletDatas, packetTail);
        braceletDataPackets.add(braceletDataPacket);
    }

    private void fillDeviceInfo(ChickenProtocol protocol, byte[] data, ByteBuf buffer) {
        //  (SIZE >= Constant.DeviceSize)
        // 设备信息
        byte headSign = buffer.readByte();
        if (headSign != Constant.HeadSign) {
            throw new IllegalArgumentException("开始字节不对");
        }
        byte cmdTypeDv = buffer.readByte();
        if (cmdTypeDv != Constant.CmdDv) {
            throw new IllegalArgumentException("设备 命令类型不对");
        }
        // V,2236.4565,N,11349.8360,E,89860404101880463402
        String gprsStr = buffer.readCharSequence(Constant.GPRSLen, Charset.forName("UTF-8")).toString();
        String[] gprsArr = gprsStr.split(",");
        // 定位状态
        String locStatus = gprsArr[0];
        // 纬度
        String latitude = gprsArr[1];
        // 纬度半球
        String latitudeHemisphere = gprsArr[2];
        // 经度
        String longitude = gprsArr[3];
        // 经度半球
        String longitudeHemisphere = gprsArr[4];
        Location location = new Location(latitude, latitudeHemisphere, longitude, longitudeHemisphere);
        // 卡号
        String sim = gprsArr[5];
        GPRS gprs = new GPRS(locStatus, location, sim);

        byte keep1 = buffer.readByte();
        byte tailSign = buffer.readByte();

        int deviceInfoChecksumIndex = buffer.readerIndex();
        int checksum1 = checksum(data, buffer, 0, deviceInfoChecksumIndex);
        DeviceInfo deviceInfo = new DeviceInfo(headSign, cmdTypeDv, gprs, keep1, tailSign, checksum1);
        protocol.setDeviceInfo(deviceInfo);
    }

    private int checksum(byte[] data, ByteBuf buffer, int headIndex, int tailIndex) {
        int sum = 0;
        for (int i = headIndex; i < tailIndex; i++) {
            sum += (data[i] & 0xff);
        }
        byte[] checksumData = new byte[Constant.ChecksumSize];
        buffer.readBytes(checksumData);
        int marksum = ((checksumData[0] & 0xff) << 8) + (checksumData[1] & 0xff);
        if (sum != marksum) throw new IllegalArgumentException((headIndex == 0 ? "设备信息" : "脚环数据") + "校验和不对");
        return marksum;
    }

    private String getStringBuffer(ByteBuf in, byte[] bytes) {
        in.readBytes(bytes);
        StringBuffer str = new StringBuffer();
        for (int j = 0; j < bytes.length; j++) {
            str.append(Integer.toHexString(bytes[j] & 0xFF));
        }
        return str.toString();
    }

    private Integer transferDistance(byte vol_dist) {
        int d = vol_dist & 0xFF % 16;
        return d * 5;
    }

    private BigDecimal transferVoltage(byte vol_dist) {
        int v = vol_dist & 0xFF / 16;
        BigDecimal voltage = new BigDecimal(v);
        // x * 0.05 + 2.4
        return voltage.multiply(new BigDecimal("0.05")).add(new BigDecimal("2.4"));
    }

    private BigDecimal transferTemperature(byte temperatureB) {
        int tem = temperatureB & 0xFF;
        if (tem > 128) {
            tem = tem - 256;
        }
        BigDecimal temperature = new BigDecimal(tem);
        // x /2 + 23
        return temperature.divide(new BigDecimal(2)).add(new BigDecimal(23));
    }
}
